package com.xms.sdk.extensions;

import com.xms.core.exception.XmsException;
import com.xms.schema.entity.Attribute;
import com.xms.schema.entity.Entity;
import com.xms.schema.entity.OptionSetDetail;
import com.xms.schema.entity.StringMap;
import com.xms.schema.extension.AttributeExt;
import com.xms.schema.service.entity.IEntityFinderService;
import com.xms.sdk.core.*;
import com.xms.utils.CollectionUtil;
import com.xms.utils.RegexUtil;
import com.xms.utils.UUIDUtil;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * EntityExt
 * </p>
 *
 * @author migo
 * @since 2021/9/6
 */
public class EntityWrapper {

    public static Object wrapAttributeValue(com.xms.core.data.Entity entity, IEntityFinderService entityFinderService, Attribute attr, Object value) throws XmsException {
        List<String> errors = new ArrayList<>();

        if (value == null || value.toString().isEmpty())
        {
            return null;
        }
        if (attr != null)
        {
            if (AttributeExt.typeIsBit(attr))
            {
                value = ValueConverter.toBoolean(value.toString());
            }
            else if (AttributeExt.typeIsRef(attr))
            {
                if (value instanceof EntityReference)
                {
                    EntityReference er = (EntityReference)value;
                    //是否存在该引用实体
                    if (!entityFinderService.existsName(er.ReferencedEntityName))
                    {
                        errors.add(String.format("referenced entity '%s' is not found by attribute '%s'", er.ReferencedEntityName, attr.getName()));
                    }
                }
                else
                {
                    if (RegexUtil.isUUID(value.toString()))
                    {
                        Entity referencedEntity = entityFinderService.getById(attr.getReferencedentityid());
                        value = new EntityReference(referencedEntity.getName(), UUIDUtil.get(value.toString()));
                    }
                    else
                    {
                        errors.add(String.format("%s's value(%s) is not valid, it's type should be 'SDK.EntityReference'", attr.getName(), value.toString()));
                    }
                }
            }
            else if (AttributeExt.typeIsPickList(attr))
            {
                //是否正确的格式
                if (!(value instanceof OptionSetValue))
                {
                    if (RegexUtil.isDigit(value.toString()))
                    {
                        value = new OptionSetValue(Integer.parseInt(value.toString()));
                    }
                    else
                    {
                        errors.add(String.format("%s's value(%s) is not valid, it's type should be 'SDK.OptionSetValue'", attr.getName(), value.toString()));
                    }
                }
                //是否存在该值
            }
            else if (AttributeExt.typeIsOwner(attr))
            {
                //是否正确的格式
                if (!(value instanceof OwnerObject))
                {
                    if (RegexUtil.isUUID(value.toString()))
                    {
                        value = new OwnerObject(OwnerTypes.SystemUser, UUIDUtil.get(value.toString()));
                    }
                    else
                    {
                        errors.add(String.format("%s's value(%s) is not valid, it's type should be 'SDK.OwnerObject'", attr.getName(), value.toString()));
                    }
                }
                //是否存在该值
            }
            else if (AttributeExt.typeIsState(attr))
            {
                value = ValueConverter.toBoolean(value.toString());
            }
            else if (AttributeExt.typeIsInt(attr))
            {
                //是否正确的格式
                if (!RegexUtil.isDigit(value.toString()))
                {
                    errors.add(String.format("%s's value(%s) is not valid, it's type should be 'int'", attr.getName(), value.toString()));
                }
                else{
                    value = Integer.parseInt(value.toString());
                }
            }
            else if (AttributeExt.typeIsFloat(attr))
            {
                //是否正确的格式
                if (!RegexUtil.isDecimals(value.toString()))
                {
                    errors.add(String.format("%s's value(%s) is not valid, it's type should be 'float'", attr.getName(), value.toString()));
                }
                else{
                    value = Float.parseFloat(value.toString());
                }
            }
            else if (AttributeExt.typeIsMoney(attr))
            {
                //是否正确的格式
                if (!(value instanceof Money))
                {
                    if (RegexUtil.isDecimals(value.toString()))
                    {
                        value = new Money(Float.parseFloat(value.toString()));
                    }
                    else
                    {
                        errors.add(String.format("%s's value(%s) is not valid, it's type should be 'SDK.Money'", attr.getName(), value.toString()));
                    }
                }
            }
            else if (AttributeExt.typeIsDateTime(attr))
            {
                //是否正确的格式
                try {
                    value = ValueConverter.toLocalDateTime(value.toString());
                }catch(Exception e){
                    errors.add(String.format("%s's value(%s) is not valid, it's type should be 'datetime'", attr.getName(), value.toString()));
                }
            }
        }
        if (errors.size() > 0)
        {
            throw new XmsException(String.join("\n", errors));
        }
        return value;
    }

    public static com.xms.core.data.Entity unWrapAttributeValue(com.xms.core.data.Entity entity)
    {
        com.xms.core.data.Entity result = new com.xms.core.data.Entity(entity.Name);
        Iterator iter = entity.entrySet().iterator();
        while(iter.hasNext())
        {
            Map.Entry entry = (Map.Entry) iter.next();
            String k = entry.getKey().toString();
            Object value = entry.getValue();
            if (value instanceof EntityReference) {
                result.setValue(k, ((EntityReference) value).ReferencedValue);
            } else if (value instanceof OptionSetValue) {
                result.setValue(k, ((OptionSetValue) value).value);
            } else if (value instanceof Money) {
                result.setValue(k, ((Money) value).value);
            } else if (value instanceof OwnerObject) {
                OwnerObject o = (OwnerObject) value;
                result.setValue(k, o.OwnerId);
                result.setValue("owneridtype", o.OwnerType);
            }
            else{
                result.setValue(k, value);
            }
        }
        return result;
    }

    public static Object unWrapAttributeValue(com.xms.core.data.Entity entity, Attribute attr)
    {
        Object value = null;
        if (entity.containsKey(attr.getName()))
        {
            Object obj = entity.get(attr.getName());
            if (obj instanceof EntityReference)
            {
                value = ((EntityReference)obj).ReferencedValue;
            }
                else if (obj instanceof Money)
            {
                value = ((Money)obj).value;
            }
                else if (obj instanceof OptionSetValue)
            {
                value = ((OptionSetValue)obj).value;
            }
                else if (obj instanceof OwnerObject)
            {
                value = ((OwnerObject)obj).OwnerId;
            }
                else if (obj instanceof Boolean)
            {
                value = (Boolean.parseBoolean(obj.toString())) ? 1 : 0;
            }
                else
            {
                value = obj;
            }
        }
        return value;
    }

    public static com.xms.core.data.Entity wrapOptionName(List<Attribute> attributes, com.xms.core.data.Entity data)
    {
        if (data == null || data.isEmpty())
        {
            return data;
        }
        if (!attributes.stream().anyMatch(n -> AttributeExt.typeIsBit(n) || AttributeExt.typeIsState(n) || AttributeExt.typeIsPickList(n)))
        {
            return data;
        }
        List<Attribute> attributes_tmp = attributes.stream().filter(n -> AttributeExt.typeIsBit(n) || AttributeExt.typeIsState(n) || AttributeExt.typeIsPickList(n)).collect(Collectors.toList());
        for (Attribute attr : attributes_tmp)
        {
            String columnName = attr.getName();
            if (data.containsKey(columnName + "name") || !data.containsKey(columnName))
            {
                continue;
            }
            Object v = data.getValue(columnName);
            if (v == null)
            {
                data.setValue(columnName + "name", "");
            }
            else
            {
                if (AttributeExt.typeIsPickList(attr) || AttributeExt.typeIsStatus(attr))
                {
                    if (attr.getOptionset() != null && CollectionUtil.notEmpty(attr.getOptionset().getItems()))
                    {
                        OptionSetDetail o = attr.getOptionset().getItems().stream().filter(n -> n.getValue() == Integer.parseInt(v.toString())).findFirst().orElse(null);
                        data.setValue(columnName + "name", o != null ? o.getName() : "");
                    }
                    else
                    {
                        data.setValue(columnName + "name", "");
                    }
                }
                else if (AttributeExt.typeIsState(attr) || AttributeExt.typeIsBit(attr))
                {
                    if (CollectionUtil.notEmpty(attr.getPicklists()))
                    {
                        int bVal = RegexUtil.isDigit(v.toString()) ? (Integer.parseInt(v.toString())) : (Boolean.parseBoolean(v.toString()) ? 1 : 0);
                        StringMap o = attr.getPicklists().stream().filter(n -> n.getValue() == bVal).findFirst().orElse(null);
                        data.setValue(columnName + "name", o != null ? o.getName() : "");
                    }
                    else
                    {
                        data.setValue(columnName + "name", "");
                    }
                }
            }
        }
        return data;
    }
}
